package org.example.infrastructure.persistent.repository;

import com.alibaba.fastjson.JSON;
import org.example.domain.activity_manage.model.entity.PrizeEntity;
import org.example.domain.activity_manage.repository.IPrizeRepo;
import org.example.infrastructure.persistent.dao.IPrizeDao;
import org.example.infrastructure.persistent.po.PrizePO;
import org.example.types.common.RedisKeysPrefix;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author lxc18
 * @date 2025/3/18 10:39
 * @description PrizeRepo
 */
@Repository
public class PrizeRepo implements IPrizeRepo {
    @Resource
    private IPrizeDao iPrizeDao;
    @Resource
    private RedissonClient redissonClient;
    @Override
    public boolean createPrize(PrizeEntity prizeEntity) {
        PrizePO prizePO = PrizePO.builder()
                .prizeId(prizeEntity.getPrizeId())
                .prizeName(prizeEntity.getPrizeName())
                .prizeDescription(prizeEntity.getPrizeDescription())
                .prizeType(prizeEntity.getPrizeType())
                .probability(prizeEntity.getProbability())
                .stockAvailable(prizeEntity.getStockAvailable())
                .stockUsed(prizeEntity.getStockUsed())
                .createTime(prizeEntity.getCreateTime())
                .updateTime(prizeEntity.getUpdateTime())
                .build();
        if (iPrizeDao.createPrize(prizePO)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean deletePrize(Long prizeId) {
        if (iPrizeDao.deletePrize(prizeId)) {
            //删除缓存中奖品信息
            String cacheKey = RedisKeysPrefix.PRIZE_INFO + prizeId;
            //库存也要删除
            String cacheKeyStock = RedisKeysPrefix.PRIZE_STOCK + prizeId;
            String cacheKeyUsedStock = RedisKeysPrefix.PRIZE_USED_STOCK + prizeId;
            try {
                redissonClient.getBucket(cacheKey).delete();
                redissonClient.getBucket(cacheKeyStock).delete();
                redissonClient.getBucket(cacheKeyUsedStock).delete();
            } catch (Exception e) {
                redissonClient.getBucket(cacheKey).expire(1, TimeUnit.SECONDS);
                redissonClient.getBucket(cacheKeyStock).expire(1, TimeUnit.SECONDS);
                redissonClient.getBucket(cacheKeyUsedStock).expire(1, TimeUnit.SECONDS);
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean updatePrize(PrizeEntity prizeEntity) {
        PrizePO prizePO = PrizePO.builder()
                .prizeId(prizeEntity.getPrizeId())
                .prizeName(prizeEntity.getPrizeName())
                .prizeDescription(prizeEntity.getPrizeDescription())
                .prizeType(prizeEntity.getPrizeType())
                .probability(prizeEntity.getProbability())
                .build();

        //库存更新只能在指定的条件下更新
        if(iPrizeDao.updatePrize(prizePO)){
            //删除缓存中奖品信息
            String cacheKey = RedisKeysPrefix.PRIZE_INFO + prizeEntity.getPrizeId();
            try {
                redissonClient.getBucket(cacheKey).delete();
            } catch (Exception e) {
                redissonClient.getBucket(cacheKey).expire(1, TimeUnit.SECONDS);
            }
            return true;
        }
        return false;
    }

    @Override
    public List<PrizeEntity> queryPrizeByName(String prizeName) {
        List<PrizeEntity> res = new ArrayList<>();
        List<PrizePO> prizePOList = iPrizeDao.queryPrizeByName(prizeName);
        for (PrizePO prizePO : prizePOList) {
            PrizeEntity prizeEntity = PrizeEntity.builder()
                        .prizeId(prizePO.getPrizeId())
                        .prizeName(prizePO.getPrizeName())
                        .prizeDescription(prizePO.getPrizeDescription())
                        .prizeType(prizePO.getPrizeType())
                        .probability(prizePO.getProbability())
                        .stockAvailable(prizePO.getStockAvailable())
                        .stockUsed(prizePO.getStockUsed())
                        .createTime(prizePO.getCreateTime())
                        .updateTime(prizePO.getUpdateTime())
                        .build();
            //实时展示奖品库存和已用库存
            //奖品剩余库存
            String cacheKey = RedisKeysPrefix.PRIZE_STOCK + prizePO.getPrizeId();
            RAtomicLong stock = redissonClient.getAtomicLong(cacheKey);
            prizeEntity.setStockAvailable((int) stock.get());
            //已用库存
            String cacheKeyUsed = RedisKeysPrefix.PRIZE_USED_STOCK + prizePO.getPrizeId();
            RAtomicLong stockUsed = redissonClient.getAtomicLong(cacheKeyUsed);
            prizeEntity.setStockUsed((int) stockUsed.get());

            res.add(prizeEntity);
        }
        return res;
    }

    @Override
    public PrizeEntity queryPrizeById(Long prizeId) {
        String cacheKey = RedisKeysPrefix.PRIZE_INFO + prizeId;
        String  jsonstr = (String) redissonClient.getBucket(cacheKey).get();
        PrizeEntity prizeEntity = JSON.parseObject(jsonstr, PrizeEntity.class);
        if (prizeEntity == null) {
            PrizePO prizePO = iPrizeDao.queryPrizeById(prizeId);
            if (prizePO != null) {
                prizeEntity = PrizeEntity.builder()
                                .prizeId(prizePO.getPrizeId())
                                .prizeName(prizePO.getPrizeName())
                                .prizeDescription(prizePO.getPrizeDescription())
                                .prizeType(prizePO.getPrizeType())
                                .probability(prizePO.getProbability())
                                .stockAvailable(prizePO.getStockAvailable())
                                .stockUsed(prizePO.getStockUsed())
                                .createTime(prizePO.getCreateTime())
                                .updateTime(prizePO.getUpdateTime())
                                .build();
                // 缓存奖品信息
                redissonClient.getBucket(cacheKey).set(JSON.toJSONString(prizeEntity));

            }

        }
        //实时展示奖品库存和已用库存
        //奖品剩余库存
        String cacheKeyStock = RedisKeysPrefix.PRIZE_STOCK + prizeEntity.getPrizeId();
        RAtomicLong stock = redissonClient.getAtomicLong(cacheKeyStock);
        prizeEntity.setStockAvailable((int) stock.get());
        //已用库存
        String cacheKeyUsed = RedisKeysPrefix.PRIZE_USED_STOCK + prizeEntity.getPrizeId();
        RAtomicLong stockUsed = redissonClient.getAtomicLong(cacheKeyUsed);
        prizeEntity.setStockUsed((int) stockUsed.get());


        return prizeEntity;
    }

    @Override
    public List<PrizeEntity> queryPrizeByPage(int pageNum, int pageSize) {
        List<PrizeEntity> res = new ArrayList<>();
        List<PrizePO> prizePOList = iPrizeDao.queryPrizeByPage(pageNum, pageSize);
        for (PrizePO prizePO : prizePOList) {
            PrizeEntity prizeEntity = PrizeEntity.builder()
                        .prizeId(prizePO.getPrizeId())
                        .prizeName(prizePO.getPrizeName())
                        .prizeDescription(prizePO.getPrizeDescription())
                        .prizeType(prizePO.getPrizeType())
                        .probability(prizePO.getProbability())
                        .stockAvailable(prizePO.getStockAvailable())
                        .stockUsed(prizePO.getStockUsed())
                        .createTime(prizePO.getCreateTime())
                        .updateTime(prizePO.getUpdateTime())
                        .build();
            //实时展示奖品库存和已用库存
            //奖品剩余库存
            String cacheKey = RedisKeysPrefix.PRIZE_STOCK + prizePO.getPrizeId();
            RAtomicLong stock = redissonClient.getAtomicLong(cacheKey);
            prizeEntity.setStockAvailable((int) stock.get());
            //已用库存
            String cacheKeyUsed = RedisKeysPrefix.PRIZE_USED_STOCK + prizePO.getPrizeId();
            RAtomicLong stockUsed = redissonClient.getAtomicLong(cacheKeyUsed);
            prizeEntity.setStockUsed((int) stockUsed.get());
            res.add(prizeEntity);
        }
        return res;
    }

    @Override
    public boolean updatePrizeStock(Long prizeId, int stockDelta) {
        //先原子性更新缓存，再更新数据库
        String cacheKey = RedisKeysPrefix.PRIZE_STOCK + prizeId;
        RAtomicLong stock = redissonClient.getAtomicLong(cacheKey);

        // 确保库存已初始化,这里不用加锁，因为不存在并发安全问题
        if (!stock.isExists()) {
            PrizePO prizePO = iPrizeDao.queryPrizeById(prizeId);
            if (prizePO != null) {
                stock.set(prizePO.getStockAvailable()); // 直接初始化库存
            } else {
                return false; // 商品不存在，更新失败
            }
        }
        stock.addAndGet(stockDelta);
        //需要更新数据库，因为库存扣减是增量更新
        //这里不用延迟队列的原因是，大量的额度会增加延迟队列负担并且，不需要流量削峰，是一个低频操作
        if (!iPrizeDao.updatePrizeStock(prizeId, stockDelta)) {
            //更新缓存
            return false;
        }
        return true;
    }

    @Override
    public int queryPrizeStock(Long prizeId) {
        String cacheKey = RedisKeysPrefix.PRIZE_STOCK + prizeId;
        RAtomicLong stock = redissonClient.getAtomicLong(cacheKey);
        // 加分布式锁防止缓存击穿
        if (!stock.isExists()) {
            boolean lockAcquired = redissonClient.getLock("init_stock_lock:" + prizeId).tryLock();
            if (lockAcquired) {
                try {
                    PrizePO prizePO = iPrizeDao.queryPrizeById(prizeId);
                    if (prizePO != null) {
                        stock.set(prizePO.getStockAvailable());
                    }
                } finally {
                    redissonClient.getLock("init_stock_lock:" + prizeId).unlock();
                }
            }
        }
        return (int) stock.get();
    }

    @Override
    public int queryPrizeUsedStock(Long prizeId) {
        String cacheKey = RedisKeysPrefix.PRIZE_USED_STOCK + prizeId;
        RAtomicLong stock = redissonClient.getAtomicLong(cacheKey);
        // 加分布式锁防止缓存击穿
        if (!stock.isExists()) {
            boolean lockAcquired = redissonClient.getLock("init_used_stock_lock:" + prizeId).tryLock();
            if (lockAcquired) {
                try {
                    PrizePO prizePO = iPrizeDao.queryPrizeById(prizeId);
                    if (prizePO != null) {
                        stock.set(prizePO.getStockUsed());
                    }
                } finally {
                    redissonClient.getLock("init_used_stock_lock:" + prizeId).unlock();
                }
            }
        }
        return (int) stock.get();
    }

}
